#ifndef _H_C_PROMISE_HELPER_H_
#define _H_C_PROMISE_HELPER_H_

#include "SPromiseProcessor.h"

namespace TB
{
	class CPromiseHelper
	{
	public:
		static SVoidPromise Delay(UINT64 qwMilliSeconds);

		template<typename T>
		static SPromise<T> Resolve(const T& oData)
		{
			return SPromise<T>(oData);
		}
		template<typename T>
		static SPromise<T> Reject(const std::shared_ptr<std::exception>& poException)
		{
			return SPromise<T>(poException);
		}
		template<typename T>
		static SPromiseProcessor<T> CreatePendingPromise()
		{
			return SPromiseProcessor<T>(false);
		}

		template<typename T>
		static SPromise<std::shared_ptr<std::vector<T>>> All(const std::vector<SPromise<T>>& roPromises)
		{
			UINT32 dwSize = (UINT32)roPromises.size();
			std::shared_ptr<std::vector<T>> poRetData = std::make_shared<std::vector<T>>(dwSize);
			if (dwSize == 0)
				return Resolve(poRetData);
			std::shared_ptr<std::atomic<UINT32>> poCount = std::make_shared<std::atomic<UINT32>>(dwSize);
			SPromiseProcessor<std::shared_ptr<std::vector<T>>> sPromiseProcessor;
			for (UINT32 i = 0; i != dwSize; ++i)
			{
				UINT32 dwIndex = i;
				const SPromise<T>& pr = roPromises[i];
				pr.Then([poRetData, poCount, sPromiseProcessor, dwIndex](const T& oData)->SPromise<bool>
				{
					(*poRetData)[dwIndex] = oData;
					if (0 == -- * poCount)
						sPromiseProcessor.Resolve(poRetData);
					return Resolve(true);
				}).Catch([sPromiseProcessor](const std::shared_ptr<std::exception>& poError)
				{
					sPromiseProcessor.Reject(poError);
					return Resolve(true);
				});
			}
			return sPromiseProcessor.GetPromise();
		}

		template<typename T1, typename T2>
		static SPromise<std::tuple<T1, T2>> All(const SPromise<T1>& rsPromise1, const SPromise<T2>& rsPromise2)
		{
			std::shared_ptr<std::tuple<T1, T2>> poRetData = std::make_shared<std::tuple<T1, T2>>();
			std::shared_ptr<std::atomic<UINT32>> poCount = std::make_shared<std::atomic<UINT32>>(2);
			SPromiseProcessor<std::shared_ptr<std::tuple<T1, T2>>> sPromiseProcessor;
			rsPromise1.Then([poRetData, poCount, sPromiseProcessor](const T1& oData)
			{
				std::get<0>(*poRetData) = oData;
				if (0 == -- * poCount)
					sPromiseProcessor.Resolve(poRetData);
				return Resolve(true);
			}).Catch([sPromiseProcessor](const std::shared_ptr<std::exception>& poError)
			{
				sPromiseProcessor.Reject(poError);
				return Resolve(true);
			});
			rsPromise2.Then([poRetData, poCount, sPromiseProcessor](const T2& oData)
			{
				std::get<1>(*poRetData) = oData;
				if (0 == -- * poCount)
					sPromiseProcessor.Resolve(poRetData);
				return Resolve(true);
			}).Catch([sPromiseProcessor](const std::shared_ptr<std::exception>& poError)
			{
				sPromiseProcessor.Reject(poError);
				return Resolve(true);
			});
			return sPromiseProcessor.GetPromise();
		}

		template<typename T1, typename T2, typename T3>
		static SPromise<std::tuple<T1, T2, T3>> All(const SPromise<T1>& rsPromise1, const SPromise<T2>& rsPromise2, const SPromise<T3>& rsPromise3)
		{
			std::shared_ptr<std::tuple<T1, T2, T3>> poRetData = std::make_shared<std::tuple<T1, T2, T3>>();
			std::shared_ptr<std::atomic<UINT32>> poCount = std::make_shared<std::atomic<UINT32>>(3);
			SPromiseProcessor<std::shared_ptr<std::tuple<T1, T2, T3>>> sPromiseProcessor;
			rsPromise1.Then([poRetData, poCount, sPromiseProcessor](const T1& oData)
			{
				std::get<0>(*poRetData) = oData;
				if (0 == -- * poCount)
					sPromiseProcessor.Resolve(poRetData);
				return Resolve(true);
			}).Catch([sPromiseProcessor](const std::shared_ptr<std::exception>& poError)
			{
				sPromiseProcessor.Reject(poError);
				return Resolve(true);
			});
			rsPromise2.Then([poRetData, poCount, sPromiseProcessor](const T2& oData)
			{
				std::get<1>(*poRetData) = oData;
				if (0 == -- * poCount)
					sPromiseProcessor.Resolve(poRetData);
				return Resolve(true);
			}).Catch([sPromiseProcessor](const std::shared_ptr<std::exception>& poError)
			{
				sPromiseProcessor.Reject(poError);
				return Resolve(true);
			});
			rsPromise3.Then([poRetData, poCount, sPromiseProcessor](const T3& oData)
			{
				std::get<2>(*poRetData) = oData;
				if (0 == -- * poCount)
					sPromiseProcessor.Resolve(poRetData);
				return Resolve(true);
			}).Catch([sPromiseProcessor](const std::shared_ptr<std::exception>& poError)
			{
				sPromiseProcessor.Reject(poError);
				return Resolve(true);
			});
			return sPromiseProcessor.GetPromise();
		}
	};
}

#endif
